"""Retrying decorator to retry method/function several times."""

import logging
import time


def retrying_on_exception(exception, retries=3, initial_delay=3):
    """Decorate method/function to be retried on exception after initial_delay.

    Do retries based on how many were set. The wait delay before next
    attempt is increased by initial_delay each time.

    Arguments:
        exception:     a type of exception to catch, e.g. RuntimeError
        retries:       max. number of times the decorated method will be run
        initial_delay: a number of seconds to wait after first exception;
                       the total number of seconds we wait is increased by this
                       amount after each retry

    """
    def wrapper(function):
        def wrapped(*args, **kwargs):
            wrapped.failed_count = 0
            wrapped.retries = retries

            delay = 0
            while True:
                delay += initial_delay

                try:
                    return function(*args, **kwargs)
                except exception:
                    wrapped.failed_count += 1

                    if wrapped.failed_count < retries:
                        logging.warning('RETRY func(%s), delaying for %ds',
                                        ', '.join([str(x) for x in args]),
                                        delay)
                        time.sleep(delay)
                        continue

                    logging.warning('RETRY giving up on func(%s)',
                                    ', '.join([str(x) for x in args]))
                    # don't sleep on last attempt, there's no point;
                    # instead raise exception
                    raise

        return wrapped

    return wrapper
